Fix OPALX accumulateHalo seg-fault in Release mode#564
Merged
Conversation
…ccess instead; similar to how FieldBufferOps already does it
… already failing?????
…xed the seg-fault before
…aves the recompilationg
…icitly long instead of int/long conversion
…worked because it prevented inlining
…ce only worked because it prevented inlining" This reverts commit b1dbe43.
aliemen
added a commit
to OPALX-project/OPALX
that referenced
this pull request
Jul 10, 2026
Collaborator
Author
|
cscs-ci run cscs-ci-gh200, cscs-ci-openmp |
Collaborator
Author
|
By the way: this is the actual diff of the PR. The rest is just clang-format noise. |
aaadelmann
approved these changes
Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#561 needs to be merged first, since this PR has branches fromAfter this PR I expect ALL weird GPU release seg-faults in OPALX to be fixed.fix-opalx-release-assignment-segfault.OPALX-project/OPALX#426 already documents a bug where OPALX seg-faults only on GPU (tested with CUDA) in Release mode during the
accumulateHalostep after thescatter()kernel. In the following I'll try to outline what the symptoms are, then what the actual bug is (because they seem a bit disconnected), and my proposed fix.What's happening and where?
When
ParticleAttribute::scatteris is called, it callsaccumulateHalo()after thescatterkernel which in turn calls the same function on theHaloCellsinstance, see here. InsideHaloCells::accumulateHalowe call the templatedexchangeBoundaries<lhs_plus_assign>(view, layout, HALO_TO_INTERNAL);. This function then callspackto load boundaries that need to be communicated into a buffer. The exact kernel that loads the buffer (see here) is where the seg-fault happens.However, the problem is not the actual content. I've checked every object and every variable for consistency (ranges, memory spaces, values, values of member variables of objects and so on), but everything is initialized correctly. I also tried every compute sanitizer tool and debugger, but without any usable output, meaning the bug does not happen on GPU, but on host during the actual kernel launch.
Now, a "circumvention" of the bug was simply adding either an empty kernel launch or a
fencebefore the templated function call, i.e.:However, putting this fence before the
BareField::accumulateHalocall insideParticleAttribute::scatter, i.e. the following, does not work:This indicates that it is not actually a (e.g.) device synchronization issue, but has something to do with how the compiler translates the call stack. Otherwise it would not depend on the placement of the
fencein this simple "linear function call chain". Note thefenceposition changes generated code and linker selection and needs to be at these specific places beforeHaloCells.Disassembling the core dump
The easiest (and turns out correct) interpretation is that the compiler optimizes something away that's then called leading to a host-side seg-fault. In that case, we need to look at what the compiler produced, so I let AI sift through the core dumps and the OPALX binary. It failed deterministically at program counter
0. The symbolized thread stack is (this we already know!):The failing instructions in the
Kokkos::parallel_forinstantiation are:x19 + 0x2c8has address0x183ee20. The symbol at that address is NVCC's translation-unit-local copy helper for the extended lambda inHaloCells<double, 3>::pack():Now the important part: The OPALX executable contains three copies of this same
fp_copierhelper:As I understand it, NVCC identified three equivalent template specializations in three translation units for the same kernel launch and optimized them by merging all three ("weak, linker-coalesced
Kokkos::parallel_for; something very similar as described here"). But since the NVCC stores the privatefp_copierhelper as a pointer to this function, the compiler didn't recognize it as something different. However, the failingpacknow called the first helper0x183ee20that was part of a instantiation that "remained uninitialized". The host-sideparallel_forlaunch mechanism therefore calls a null function pointer before the CUDA kernel launch.During compilation, NVCC determines that concrete code is needed for
HaloCells<double, 3>::pack(...). It therefore generates one copy of the template machinery and one private lambda-helper state. The same process happens independently for all three translation units (below I mentioned why there are at all three different translation units):As mentioned before and as evident from the binary itself: the first two "unit-local
fp_copier" are "optimized away", but theBinnedFieldSolver.cpptranslation unit then links/runs against the very firstfp_copierofPartBunch(which in turn is nowNULL...).Why does the bug only appear in OPALX and not in IPPL?
Looking at e.g. the
LandauDampingbinary, we see the same translation unit only once. The problem is that this specific part of IPPL is included three times in OPALX:PartBunch, i.e.template class PartBunch<double, 3>;.FieldSolver<double, 3>::initOpenSolver().template class BinnedFieldSolver<double, 3>;.These are three distinct translation units that contain (through
ParticleAttributeand thenBareFieldandHaloCells) thispack()call leading to the issue mentioned in the previous section.The fix
The fix replaces NVCC extended lambdas with named functor types. These store the Kokkos views directly and use default C++ copy construction. Therefore, the duplicate template instantiation can be safely merged by the linker without depending on the private (private meaning "per translation unit")
fp_copierorfp_deleterhelpers.The original code needed these helpers, because the following pattern constructs the already mentioned unnamed extended lambda:
...which needs these
fp_copierandfp_deleterhelpers.unpack()andapplyPeriodicSerialDim()use the same extended-lambda pattern and also need to be changed withpack()or they can seg-fault in a similar way.Testing
I ran
SwissFEL-booster-scon 16 ranks as well as all regression tests in release on 2 GH200 ranks without any of the previous OPALX "bug circumventions". Everything works fine as expected.Additional comments
I also included a small potentially uninitialized variable fix:
BufferHandler.hhas two membersusedSize_mandfreeSize_mwhich are in some instances not instantiated, but first accessed viausedSize_m += value(without any assignment first). This did not lead to bug, but could be dangerous.Finally, I am pretty sure that what we do in OPALX (extended lambdas behind multiple translation units on heavily templated code) is completely legal, but exposed some kind of Compiler bug. Note that I don't know what the actual NVCC bug could be, but it looks like perhaps the compiler was simply overwhelmed.